package mailSendService.module;

import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.io.RASInputStream;
import com.itextpdf.text.io.RandomAccessSourceFactory;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.*;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.List;

public class PdfSignUtil {


    public static void signPDF(String pdfPath, String pdfOutPath, String moulagePath, String certPath, String password){

        try
        {
            byte [] certData = FileUtil.localBufferRead(certPath); //证书流

            KeyStore ks = CertUtil.getKeyStoreByPfx(certData, password);
            Certificate[] certChain = CertUtil.getCertificateChainByKeyStore(ks);
            PrivateKey privateKey = CertUtil.getPrivateKeyByKeyStore(ks, password);

            //读取待签名pdf
            byte[] fileData = FileUtil.localBufferRead(pdfPath);

            ByteArrayInputStream fbis = new ByteArrayInputStream(fileData); //输入流
            PdfReader reader = new PdfReader(fbis); //阅读者


            ByteArrayOutputStream faos = new ByteArrayOutputStream(); //输出流

            //create signature private
            PdfStamper stamper = PdfStamper.createSignature(reader, faos, '0', null, true); //创建当前位置信息签名
            PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); //签名附加信息
            appearance.setReason("这里设置签名的原因");
            appearance.setLocation("这里设置签名的地址");

            FileInputStream imgIns = new FileInputStream(moulagePath);
            java.nio.ByteBuffer byteBuffer = java.nio.ByteBuffer.allocate(imgIns.available());
            imgIns.getChannel().read(byteBuffer);
            imgIns.close();
            PdfSignUtil.doImageStamp(stamper,byteBuffer.array(), 160, reader.getPageSize(1).getHeight() - 120, 260, reader.getPageSize(1).getHeight() - 20, 1);
            stamper.getWriter().setCompressionLevel(5);
            // 摘要算法
            ExternalDigest digest = new BouncyCastleDigest();
            BouncyCastleProvider provider = new BouncyCastleProvider();
            Security.addProvider(provider);
            ExternalSignature externalSignature = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA1, provider.getName()); //签名过程 -- 签名提供者
            //ExternalSignature externalSignature = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA1, null);
            MakeSignature.signDetached(appearance, digest, externalSignature, certChain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
            FileUtil.localBufferWrite(faos.toByteArray(), pdfOutPath); //输出流

        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    //签章
    public static void doImageStamp(PdfStamper pdfStamper, byte[] imgBytes, float leftBottomX, float leftBottomY, float imgWidth, float imgHeight, int pageNum){
        try {
            ImageData imageData = ImageDataFactory.create(imgBytes);
            PdfSignatureAppearance appearance = pdfStamper.getSignatureAppearance();
            //四个参数的分别是，图章左下角x，图章左下角y，图章右上角x，图章右上角y
            Rectangle rectangle = new Rectangle(leftBottomX, leftBottomY, imgWidth, imgHeight);
            appearance.setVisibleSignature(rectangle, pageNum, "Signature1");
            appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
            appearance.setSignatureGraphic(Image.getInstance(imgBytes));
            //没有被签章的
            appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
        } catch (BadElementException | IOException e) {
            e.printStackTrace();
        }
    }

    //获取签名信息
    public static byte[] getDigitalSignature(PdfReader pdfReader, String signedName){
        PdfDictionary pdfDictionary = pdfReader.getAcroFields().getSignatureDictionary(signedName);
        PdfString contexts = pdfDictionary.getAsString(PdfName.CONTENTS);
        return contexts.getBytes();
    }

    //获取元数据
    public static byte[] getOriginData(PdfReader pdfReader, String signedName){
        byte[] originDate = null;
        try {
            PdfDictionary pdfDictionary = pdfReader.getAcroFields().getSignatureDictionary(signedName);
            PdfArray pdfArray = pdfDictionary.getAsArray(PdfName.BYTERANGE);
            RandomAccessFileOrArray randomAccessFileOrArray = pdfReader.getSafeFile();
            InputStream rg = new RASInputStream(new RandomAccessSourceFactory().createRanged(randomAccessFileOrArray.createSourceView(), pdfArray.asLongArray()));
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buf = new byte[128];
            int n = 0;
            while(-1 != (n = rg.read(buf))){
                outputStream.write(buf, 0, n);
            }
            originDate = outputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return originDate;
    }

    //验签
    public static boolean verifyPDF(String pdfOutPath, String certPath, String password){
        boolean vertify = false;
        try {
            PdfReader reader = new PdfReader(pdfOutPath); //阅读者

            byte [] certData = FileUtil.localBufferRead(certPath); //证书流
            X509Certificate ks = CertUtil.getCertificateByPfx(certData, password);
            //获取签名字段
            List<String> keys = reader.getAcroFields().getSignatureNames();
            for (String signedName : keys){
                byte[] sign = PdfSignUtil.getDigitalSignature(reader, signedName);
                byte[] origin = PdfSignUtil.getOriginData(reader, signedName);
                vertify = CertUtil.PKCS7Verify(ks, origin, sign);
                if (!vertify) break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return vertify;
    }

}
